/* eslint-disable no-param-reassign */
/* eslint-disable import/no-extraneous-dependencies */
/*
 * @Description  : create命令
 * @Autor        : yanwang
 * @CreateDate   : 2023-05-22 09:57:58
 * @LastEditors  : yanwang
 * @LastEditTime : 2023-05-25 17:10:39
 */

const path = require('path');
const fs = require('fs-extra');
const inquirer = require('inquirer');
let ncp = require('ncp'); // 拷贝文件工具
const MetalSmith = require('metalsmith');
let { render } = require('consolidate').ejs;
const { promisify } = require('util');
const { downLoadDirectory, CLICONFIG } = require('../constants');
const { fetchTemplates, downloadTemplate } = require('../git');
const { waitFnloading, retry, updatePackage } = require('../util');

ncp = promisify(ncp);
render = promisify(render);

// 获取模版列表
const getTemplates = async () => {
  const data = await fetchTemplates();
  if (!data) {
    process.exit();
    return false;
  }
  const longTemplates = data.map((i) => `${i.name}(${i.describe})`);
  return longTemplates;
};

// 将模版文件下载到临时目录
const download = async (projectName, curTemplate) => {
  const dest = `${downLoadDirectory}/${curTemplate.name}`; // 暂时存储到此目录
  // TODO 判断目标文件夹是否已存在, 和gitlab做对比，有则直接返回

  const loadDown = waitFnloading(downloadTemplate, '下载模版...');
  await retry(loadDown, 5, 2)(curTemplate, dest);
  return dest; // 返回下载的最终目录
};

// 判断是否存在同名项目
const judgeExist = async (projectName, option) => {
  const target = path.resolve(projectName);
  const has = await fs.exists(target);
  // 如果目录非空且没有 -f 参数
  if (has && !option.force) {
    const { overWrite } = await inquirer.prompt({
      name: 'overWrite',
      type: 'list',
      message: '该目录存在同名项目，是否覆盖？',
      choices: ['yes', 'no'],
    });

    if (overWrite === 'no') return false;
  }
  await fs.emptyDir(target);
  return true;
};

module.exports = async (option, projectName) => {
  try {
    /** 判断是否存在同名项目 */
    const canContinue = await judgeExist(projectName, option);
    if (!canContinue) {
      process.exit();
      return;
    }

    /** 获取模版列表 */
    const longTemplates = await waitFnloading(getTemplates, '获取模版列表...')();

    /** 选择要下载的模版 */
    const { longTemplate } = await inquirer.prompt({
      name: 'longTemplate',
      type: 'list',
      message: '请选择创建项目的模版',
      choices: longTemplates,
    });

    const { templates } = require(`${CLICONFIG}/config.js`);
    const curTemplate = templates.filter((i) => i.name === longTemplate.split('(')[0])[0];
    /** 下载模版, 返回模版存放地址 */
    const result = await download(projectName, curTemplate);

    /** 处理文件 */
    if (!fs.existsSync(path.join(result, 'ask.json'))) {
      // 如果不存在ask.js则直接拷贝项目到产出目录
      await ncp(result, path.resolve(projectName));
      await updatePackage(path.resolve(projectName), { name: projectName });
    } else {
      // 如果存在ask.js，则根据用户交互，更改项目，再产出
      await new Promise((resolve, reject) => {
        MetalSmith(__dirname) // 工作目录
          .source(result) // 操作的源文件
          .destination(path.resolve(projectName)) // 目的地
          /** 根据ask.json获得用户输入，将值存到metadata中 */
          .use(async (files, metalsmith, done) => { // 增加一个插件函数到栈中
            const args = require(path.join(result, 'ask.json'));
            const obj = await inquirer.prompt(args);
            const meta = metalsmith.metadata(); // Get or set the global metadata
            Object.assign(meta, { ...obj, name: projectName || 'react-templete' });
            delete files['ask.json'];
            done();
          })
          /** 更具metadata中的值，替换模版中变量 */
          .use((files, metal, done) => {
            const obj = metal.metadata();
            Reflect.ownKeys(files).forEach(async (file) => {
              // 是要处理的文件
              if (file.includes('js') || file.includes('json')) {
                let content = files[file].contents.toString(); // 文件的内容
                if (content.includes('<%')) {
                  content = await render(content, obj);
                  files[file].contents = Buffer.from(content); // 渲染结果
                }
              }
            });
            done();
          })
          .build((err) => {
            if (err) {
              reject();
            } else {
              resolve();
            }
          });
      });

      // const args = require(path.join(result, 'ask.json'));
      // const obj = await inquirer.prompt(args);
      // await ncp(result, path.resolve(projectName));
      // fs.remove(path.resolve(projectName, 'ask.json'));
      /** 更改package.json */
      // await updatePackage(path.resolve(projectName), {name: projectName, ...obj });
    }
  } catch (e) {
    console.log(e);
    process.exit();
  }
};
